package la.iok.hzsvn.lewin.movie.cloud.service;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import la.iok.hzsvn.lewin.movie.cloud.entity.OrgUnit;
import la.iok.hzsvn.lewin.movie.cloud.entity.Room;
import la.iok.hzsvn.lewin.movie.cloud.mapper.RoomMapper;
import la.iok.hzsvn.lewin.movie.cloud.model.RoomParam;
import la.iok.hzsvn.lewin.movie.core.RoleEnum;
import la.iok.hzsvn.lewin.movie.core.Token;
import la.iok.hzsvn.lewin.movie.core.exception.ErrorCode;
import la.iok.hzsvn.lewin.movie.core.model.FinancialAccountVo;
import la.iok.hzsvn.lewin.movie.core.model.RoomVo;
import la.iok.hzsvn.lewin.movie.services.MovieServiceInvoker;
import la.iok.hzsvn.lewin.mybatis.model.PageVo;
import la.iok.hzsvn.lewin.mybatis.service.BaseServiceImpl;
import la.iok.hzsvn.share.annotations.NotNull;
import la.iok.hzsvn.share.annotations.NotNullElement;
import la.iok.hzsvn.share.annotations.Nullable;
import la.iok.hzsvn.share.collection.IdOperators;
import la.iok.hzsvn.share.utils.ArrayUtils;
import la.iok.hzsvn.share.utils.CollectionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class RoomServiceImpl extends BaseServiceImpl<Room,RoomMapper,Long> implements RoomService{
    private final OrgUnitService orgUnitService;
    private final MovieServiceInvoker movieServiceInvoker;

    public RoomServiceImpl(OrgUnitService orgUnitService, RoomMapper roomMapper,
                           MovieServiceInvoker movieServiceInvoker) {
        super(roomMapper);
        this.orgUnitService = orgUnitService;
        this.movieServiceInvoker = movieServiceInvoker;
    }

    @Override
    public Long save(@NotNull Token token, @NotNull RoomParam room) {
        String name = ErrorCode.NAME_BLANK_ERROR.assertNotBlank(room.getName());
        ErrorCode.ORG_UNIT_EMPTY_ERROR.assertNotNull(room.getOrgUnitId());
        validateAuthority(token, room.getOrgUnitId());
        Room r = new Room();
        r.setName(name);
        r.setOrgUnitId(room.getOrgUnitId());
        mapper.insertSelective(r);
        return r.getId();
    }

    private void validateAuthority(@NotNull Token token, @NotNull Long orgUnitId) {
        if(token.hasAuthority(RoleEnum.Admin)){
            //都可以
        }else if(token.hasAuthority(RoleEnum.Manager)){
            //工程人员,只能操作自己管理的店铺
            OrgUnit orgUnit = orgUnitService.select(orgUnitId);
            ErrorCode.ORG_UNIT_NOT_EXIST.assertNotNull(orgUnit, orgUnitId);
            Set<Long> set = new HashSet<>(orgUnitService.childOrgUnitIds(token.getOrgUnitId()));
            set.add(token.getOrgUnitId());
            ErrorCode.ORG_UNIT_ACCESS_DENIED.assertTrue(set.contains(orgUnit.getId()),orgUnit.getName());
        }else if(token.hasAuthority(RoleEnum.ShopManager)){
            //店长,只能在自己组织内创建
            ErrorCode.ORG_UNIT_ACCESS_DENIED.assertEqual(token.getOrgUnitId(), orgUnitId, orgUnitId);
        }else{
            ErrorCode.ORG_UNIT_ACCESS_DENIED.fail(orgUnitId);
        }
    }

    @Override
    public void update(Long id, String name) {
        if(id == null){
            return;
        }
        name = ErrorCode.NAME_BLANK_ERROR.assertNotBlank(name);
        mapper.updateProperty(id,"name", name);
    }

    @Transactional
    @Override
    public void delete(Long id, Token token) {
        Room room = select(id);
        ErrorCode.ROOM_NOT_EXIST.assertNotNull(room,id);
        validateAuthority(token,room.getOrgUnitId());
        delete(id);
    }

    @Transactional
    @Override
    public void update(@NotNull Token token, @Nullable Long id, @NotNull RoomParam param) {
        Room room = select(id);
        ErrorCode.ROOM_NOT_EXIST.assertNotNull(room,id);
        validateAuthority(token,room.getOrgUnitId());
        update(id,param.getName());
    }

    @Override
    public PageVo<RoomVo> page(int page, int limit, Token token, Long orgUnitId, String name) {
        Page<Room> list = null;
        if(token.hasAuthority(RoleEnum.Admin)){
            list = PageHelper.startPage(page,limit);
            if(orgUnitId != null){
                mapper.list(Collections.singleton(orgUnitId),name);
            }else{
                mapper.list(null,name);
            }
        }else{
            Collection<Long> orgUnitIds = new HashSet<>(orgUnitService.childOrgUnitIds(token.getOrgUnitId()));
            orgUnitIds.add(token.getOrgUnitId());
            if(orgUnitId != null && orgUnitId > 0){
                if(orgUnitIds.contains(orgUnitId)){
                    orgUnitIds = Collections.singleton(orgUnitId);
                }else{
                    return new PageVo<>(limit,page);
                }
            }
            if(orgUnitIds.isEmpty()){
                return new PageVo<>(limit,page);
            }
            list = PageHelper.startPage(page,limit);
            mapper.list(orgUnitIds,name);
        }
        PageVo<RoomVo> pageVo = new PageVo<>();
        pageVo.setPages(list.getPages());
        pageVo.setTotal(list.getTotal());
        pageVo.setCurrent(list.getPageNum());
        pageVo.setSize(list.getPageSize());
        pageVo.setRecords(po2vo(list));
        return pageVo;
    }

    @Override
    public void updateLevel(@Nullable Long id, @Nullable Long version, int level, @Nullable Date expireTime) {
        ErrorCode.PROPERTY_CHECK_ERROR.assertNotNull(id,"id","房间id不能为空");
        ErrorCode.PROPERTY_CHECK_ERROR.assertNotNull(version,"版本","版本不能为空");
        ErrorCode.PROPERTY_CHECK_ERROR.assertNotNull(expireTime,"有效期","房间级别的过期时间不能为空");
        ErrorCode.PROPERTY_CHECK_ERROR.assertTrue(level == Room.LEVEL_CLASSIC || level == Room.LEVEL_VIP,()->new Object[]{"房间级别","房间级别["+level+"]错误"});
        ErrorCode.PROPERTY_CHECK_ERROR.assertTrue(expireTime.after(new Date()),()->{
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            return new Object[]{"有效期",String.format("房间级别的过期时间[%s]必须大于当前时间",sdf.format(expireTime))};
        });
        int c = mapper.updateLevel(id,version,level,expireTime);
        ErrorCode.RECORD_MODIFIED_ERROR.assertEqual(1,c,id);
    }

    @Nullable
    @Override
    public RoomVo po2vo(@Nullable Room room) {
        if(room == null){
            return null;
        }
        List<FinancialAccountVo> list = movieServiceInvoker.roomAccounts(Collections.singleton(room.getId()));
        return po2vo(room,orgUnitService.select(room.getOrgUnitId()), CollectionUtils.isEmpty(list)?null:list.get(0));
    }
    @NotNull
    @NotNullElement
    @Override
    public List<RoomVo> rooms(@Nullable Long[] ids) {
        List<Room> list = ArrayUtils.runWhenNotEmptyWithTrimNullToSet(ids,Collections::emptyList,set->listInProperty("id",set));
        return po2vo(list);
    }

    @Override
    public List<RoomVo> shopRooms(Long[] orgUnitIds) {
        if(orgUnitIds == null || orgUnitIds.length == 0){
            return Collections.emptyList();
        }
        Set<Long> set = Arrays.stream(orgUnitIds).filter(Objects::nonNull).collect(Collectors.toSet());
        if(set.isEmpty()){
            return Collections.emptyList();
        }
        List<Room> list = listInProperty("orgUnitId", set);
        return po2vo(list);
    }

    private RoomVo po2vo(@NotNull Room room,List<OrgUnit> orgUnits,@NotNull @NotNullElement List<FinancialAccountVo> accounts){
        OrgUnit o = IdOperators.find(orgUnits,room.getOrgUnitId());
        FinancialAccountVo account = accounts.stream().filter(item->Objects.equals(room.getId(),item.getRoomId())).findFirst().orElse(null);
        return po2vo(room, o, account);
    }

    private List<RoomVo> po2vo(Collection<Room> entities){
        List<OrgUnit> orgUnits = orgUnitService.list(entities.stream().map(Room::getOrgUnitId).collect(Collectors.toSet()));
        List<FinancialAccountVo> list = movieServiceInvoker.roomAccounts(entities.stream().map(Room::getId).collect(Collectors.toSet()));
        return entities.stream().map(item->po2vo(item,orgUnits,list)).collect(Collectors.toList());
    }

    private RoomVo po2vo(@NotNull Room room, @Nullable OrgUnit o, FinancialAccountVo account) {
        RoomVo vo = new RoomVo();
        BeanUtils.copyProperties(room,vo);
        if(o !=null){
            vo.setOrgUnitId(o.getId());
            vo.setOrgUnitName(o.getName());
        }
        if(account != null){
            vo.setBalance(account.getBalance());
            vo.setLuckyMoney(account.getLuckyMoney());
        }
        return vo;
    }
}
